package com.xbongbong.paas.service.impl;

import com.alibaba.fastjson.JSON;
import com.xbongbong.lfasr.client.LfasrClientImp;
import com.xbongbong.lfasr.exception.LfasrException;
import com.xbongbong.lfasr.model.LfasrType;
import com.xbongbong.lfasr.model.Message;
import com.xbongbong.lfasr.model.ProgressStatus;
import com.xbongbong.paas.pojo.RecordContentPojo;
import com.xbongbong.paas.pojo.XunFeiTranslateDataPojo;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisConstant;
import com.xbongbong.paas.service.AudioTranslatorService;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.constant.callcenter.CallCenterConstant;
import com.xbongbong.saas.domain.entity.CallCenterCallLogEntity;
import com.xbongbong.saas.model.CallCenterCallLogModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 讯飞语音转写服务类
 *
 * @author 章华隽
 * @mail nefeed@163.com
 * @time 2018-01-10 11:10
 */
@Service("xunFeiAudioTranslatorService")
public class XunFeiAudioTranslatorServiceImpl implements AudioTranslatorService {

    private static final Logger LOG = LoggerFactory.getLogger(XunFeiAudioTranslatorServiceImpl.class);

    @Resource
    private CallCenterCallLogModel callCenterCallLogModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;



    /**
     * 语音转写本地路径的电话录音音频
     *
     * @param callCenterCallLogEntity 通话录音持久层对象
     * @param filePath                本地音频路径
     *
     * @author 章华隽
     * 创建时间：2018/1/11 上午10:17
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    @Override
    public void translateLocalPhoneAudio(CallCenterCallLogEntity callCenterCallLogEntity, String filePath) throws XbbException{
        translateLocalAudio(callCenterCallLogEntity, filePath, LfasrType.LFASR_TELEPHONY_RECORDED_AUDIO);
    }

    /**
     * 语音转写本地路径的普通音频
     *
     * @param callCenterCallLogEntity 通话录音持久层对象
     * @param filePath                本地音频路径
     *
     * @author 章华隽
     * 创建时间：2018/1/11 上午10:17
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    @Override
    public void translateLocalStandardAudio(CallCenterCallLogEntity callCenterCallLogEntity, String filePath) throws XbbException{
        translateLocalAudio(callCenterCallLogEntity, filePath, LfasrType.LFASR_STANDARD_RECORDED_AUDIO);
    }

    /**
     * 语音转写本地路径音频
     *
     * @param callCenterCallLogEntity 通话录音持久层对象
     * @param filePath                本地音频路径
     * @param type                    转写类型选择：标准版和电话版分别为： LfasrType.LFASR_STANDARD_RECORDED_AUDIO 和 LfasrType.LFASR_TELEPHONY_RECORDED_AUDIO
     *
     * @author 章华隽
     * 创建时间：2018/1/11 上午10:17
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    private void translateLocalAudio(CallCenterCallLogEntity callCenterCallLogEntity, String filePath, LfasrType type) throws XbbException {
        String corpid = callCenterCallLogEntity.getCorpid();
        Long logId = callCenterCallLogEntity.getId();
        LfasrClientImp lc;
        try {
            lc = LfasrClientImp.initLfasrClient();
        } catch (LfasrException e) {
            // 初始化异常，解析异常描述信息
            Message initMsg = JSON.parseObject(e.getMessage(), Message.class);
            LOG.error("初始化语音转写客户端失败，errNo(%s)，failed(%s)", initMsg.getErr_no(), initMsg.getFailed());
            return;
        }

        // 获取上传任务ID
        String taskId = "";
        HashMap<String, String> params = new HashMap<>(1);
        /* has_participle: 是否需要分词，true：需要对结果进行分词处理、false：不需要对结果进行分词处理，可为空。
         * max_alternatives:多候选词个数，1-10整数，表示返回结果中针对多候选词的个数限制，
         * 如设置3，则会按分词的置信度排名提供3个或3个以内的候选词，另注意使用候选词功能的话，结果集句子将不会携带标点符号。
         * 候选词的优先级高于has_participle，返回的json结果集中wordsResultList下的alternativeList字段中包含候选词列表，
         * 以供解析获取。
         */
        params.put("has_participle", "false");
        try {
            // 上传音频文件
            Message uploadMsg = lc.lfasrUpload(filePath, type, params);

            // 判断返回值
            int ok = uploadMsg.getOk();
            if (ok == 0) {
                // 创建任务成功
                taskId = uploadMsg.getData();
                LOG.info("创建讯飞语音转写任务成功，taskId（%s）", taskId);
                // 将"执行中"状态写入Redis缓存
                paasRedisHelper.setValue(CallCenterConstant.REDIS_KEY_CALL_LOG_TRANSLATE_STATUS,
                        corpid + StringConstant.COLON + logId, StringConstant.WAITING, RedisConstant.COMMON_DURATION);
            } else {
                // 创建任务失败-服务端异常
               LOG.error("创建讯飞语音转写任务失败，errNo(%s)，failed(%s)", uploadMsg.getErr_no(), uploadMsg.getFailed());

                // 将"失败"状态写入Redis缓存
                paasRedisHelper.setValue(CallCenterConstant.REDIS_KEY_CALL_LOG_TRANSLATE_STATUS,
                        corpid + StringConstant.COLON + logId, StringConstant.FAILED, RedisConstant.COMMON_DURATION);
            }
        } catch (LfasrException e) {
            // 上传异常，解析异常描述信息
            Message uploadMsg = JSON.parseObject(e.getMessage(), Message.class);
            LOG.error("上传音频文件到讯飞服务器失败，errNo(%s)，failed(%s)", uploadMsg.getErr_no(), uploadMsg.getFailed());

            // 将"失败"状态写入Redis缓存
            paasRedisHelper.setValue(CallCenterConstant.REDIS_KEY_CALL_LOG_TRANSLATE_STATUS,
                    corpid + StringConstant.COLON + logId, StringConstant.FAILED, RedisConstant.COMMON_DURATION);
        }

        // 执行次数
        int times = 0;
        final int maxTimes = 200;
        // 等待时长（单位：秒）
        int sleepSecond = 15;
        // 循环等待音频处理结果
        while (true) {
            if (++times > maxTimes) {
                LOG.error("讯飞语音转写任务执行次数超过上限（%d次）...，taskId（%s）", maxTimes, taskId);

                // 将"失败"状态写入Redis缓存
                paasRedisHelper.setValue(CallCenterConstant.REDIS_KEY_CALL_LOG_TRANSLATE_STATUS,
                        corpid + StringConstant.COLON + logId, StringConstant.FAILED, RedisConstant.COMMON_DURATION);
                return;
            }
            try {
                // 睡眠1min。另外一个方案是让用户尝试多次获取，第一次假设等1分钟，获取成功后break；失败的话增加到2分钟再获取，获取成功后break；再失败的话加到4分钟；8分钟；……
                Thread.sleep(sleepSecond * 1000L);
                LOG.info("讯飞语音转写任务执行中...，taskId（%s）", taskId);
            } catch (InterruptedException e) {
                LOG.error("线程休眠失败，%s", e);
                // 恢复中断状态
                Thread.currentThread().interrupt();
            }
            try {
                // 获取处理进度
                Message progressMsg = lc.lfasrGetProgress(taskId);

                // 如果返回状态不等于0，则任务失败
                if (progressMsg.getOk() != 0) {
                    LOG.error("讯飞语音转写任务失败，taskId（%s），errNo(%s)，failed(%s)", taskId, progressMsg.getErr_no(), progressMsg.getFailed());

                    // 服务端处理异常-服务端内部有重试机制（不排查极端无法恢复的任务）
                    // 客户端可根据实际情况选择：
                    // 1. 客户端循环重试获取进度
                    // 2. 退出程序，反馈问题
                    continue;
                } else {
                    ProgressStatus progressStatus = JSON.parseObject(progressMsg.getData(), ProgressStatus.class);
                    if (progressStatus.getStatus() == 9) {
                        // 处理完成
                        LOG.info("讯飞语音转写任务处理完成，taskId（%s）", taskId);
                        break;
                    } else {
                        // 未处理完成
                        LOG.info("讯飞语音转写任务处理仍未完成，taskId（%s），status（%s）", taskId, progressStatus.getDesc());
                        continue;
                    }
                }
            } catch (LfasrException e) {
                // 获取进度异常处理，根据返回信息排查问题后，再次进行获取
                Message progressMsg = JSON.parseObject(e.getMessage(), Message.class);
                LOG.error("获取讯飞语音转写任务精度失败，taskId（%s），errNo(%s)，failed(%s)", taskId, progressMsg.getErr_no(), progressMsg.getFailed());
            }
        }

        // 获取任务结果
        try {
            Message resultMsg = lc.lfasrGetResult(taskId);
            // 如果返回状态等于0，则任务处理成功
            if (resultMsg.getOk() == 0) {
                String data = resultMsg.getData();
                // 打印转写结果
                LOG.info("语音转写成功：%s", data);
                List<XunFeiTranslateDataPojo> translateDataPojoList = JSON.parseArray(data, XunFeiTranslateDataPojo.class);
                String translateDataStr = JSON.toJSONString(translateDataPojoList);

                List<RecordContentPojo> recordContents = formatTranslateData2RecordContents(callCenterCallLogEntity.getCallTime() + callCenterCallLogEntity.getRingTime(), translateDataPojoList);
                String recordContentsStr = JSON.toJSONString(recordContents);
                LOG.info("根据语音转写转化得到的对话内容：%s", recordContentsStr);

                // 保存翻译成功的结果
                callCenterCallLogEntity.setTranslatePlatform(StringConstant.PLATFORM_XUN_FEI);
                callCenterCallLogEntity.setTranslateData(translateDataStr);
                callCenterCallLogEntity.setRecordContent(recordContentsStr);
                callCenterCallLogModel.update(callCenterCallLogEntity);

                // 将"成功"状态写入Redis缓存
                paasRedisHelper.setValue(CallCenterConstant.REDIS_KEY_CALL_LOG_TRANSLATE_STATUS,
                        corpid + StringConstant.COLON + logId, StringConstant.SUCCESS, RedisConstant.COMMON_DURATION);
            } else {
                // 转写失败，根据失败信息进行处理
               LOG.error("语言转写失败，errNo(%s)，failed(%s)", resultMsg.getErr_no(), resultMsg.getFailed());

                // 将"失败"状态写入Redis缓存
                paasRedisHelper.setValue(CallCenterConstant.REDIS_KEY_CALL_LOG_TRANSLATE_STATUS,
                        corpid + StringConstant.COLON + logId, StringConstant.FAILED, RedisConstant.COMMON_DURATION);
            }
        } catch (LfasrException e) {
            // 获取结果异常处理，解析异常描述信息
            Message resultMsg = JSON.parseObject(e.getMessage(), Message.class);
            LOG.error("语言转写出现异常，errNo(%s)，failed(%s)", resultMsg.getErr_no(), resultMsg.getFailed());

            // 将"失败"状态写入Redis缓存
            paasRedisHelper.setValue(CallCenterConstant.REDIS_KEY_CALL_LOG_TRANSLATE_STATUS,
                    corpid + StringConstant.COLON + logId, StringConstant.FAILED, RedisConstant.COMMON_DURATION);
        }

    }

    /**
     * 根据翻译结果得到对话内容队列
     *
     * @param startTime             接通电话的时间
     * @param translateDataPojoList 翻译结果内容
     *
     * @return 对话内容队列
     * @author 章华隽
     * 创建时间：2018/1/11 下午6:58
     * 修改时间：
     * @version 3.17
     * @since 3.17
     */
    private List<RecordContentPojo> formatTranslateData2RecordContents(Integer startTime, List<XunFeiTranslateDataPojo> translateDataPojoList) {
        List<RecordContentPojo> recordContents = new ArrayList<>();
        for (XunFeiTranslateDataPojo it : translateDataPojoList) {
            int seconds = 0;
            if (it.getBg() != null) {
                seconds = (int) (it.getBg() / 1000L);
            }
            recordContents.add(new RecordContentPojo(it.getSpeaker(), it.getOnebest(), startTime + seconds));
        }
        return recordContents;
    }
}
